import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector";
import { IDragEventData, IDragProgressEventData } from "../config/abstract-drag-config";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Scene } from '@babylonjs/core/scene';
import { Camera } from "@babylonjs/core/Cameras/camera";
import { Utils } from '../../tool/utils';


/**
 * 拖动约束抽象类
 * 根据约束条件修正指针在世界坐标系下的位置，拖动配置将使用修正后的结果应用于拖动对象上；
 */
abstract class AbstractDragLimit {

    private _matrix: Matrix = new Matrix();
    private _vector: Vector3 = new Vector3();
    protected updateCall: { (target: TransformNode, manual: boolean, ...params: any): void } | null = null;

    public readonly id = Utils.id;
    public previousPosition: Vector3 = new Vector3();
    public currentPosition: Vector3 = new Vector3();
    public readonly startOffset: Vector3 = new Vector3(0, 0, 0);
    public abstract get limitType(): string;

    /**
     * 设置约束条件
     * @param params 
     */
    public abstract setInformation(...params: any): void;

    /**
     * 用给定的控制参数更新目标
     * @param target 拖动目标
     * @param params 控制参数
     */
    public abstract update(target: TransformNode, ...params: any): void;

    /**
     * 提取控制参数
     */
    public abstract extract(): any;

    /**
     * 拖动中的回调函数
     * @param func (target:目标;manual:手动触发;...params:控制参数):void
     */
    public abstract set onUpdate(func: { (target: TransformNode, manual: boolean, ...params: any): void });

    /**
     * 拖动开始时的修正行为
     * @param target 目标
     * @param eventData 事件对象
     */
    public setStart(target: TransformNode, eventData: IDragEventData): void {
        this.currentPosition.copyFrom(eventData.dragPlanePoint);
        this.previousPosition.copyFrom(eventData.dragPlanePoint);
    }

    /**
     * 拖动过程中的修正行为
     * @param target 目标
     * @param eventData 事件对象
     */
    public setProgress(target: TransformNode, eventData: IDragProgressEventData): void {
        this.previousPosition.copyFrom(this.currentPosition);
        this.currentPosition.copyFrom(eventData.dragPlanePoint);
    }

    /**
     * 拖动结束时的修正行为
     * @param target 目标
     * @param eventData 事件对象
     */
    public setEnd(target: TransformNode, eventData: IDragEventData): void {
        this.previousPosition.copyFrom(this.currentPosition);
        this.currentPosition.copyFrom(eventData.dragPlanePoint);
    }

    /**
     * 检测并获取活动相机
     * @param scene 场景
     * @returns 活动相机
     */
    protected getActiveCamera(scene: Scene): Camera | null {
        const camera = scene.activeCamera;
        if (!camera) {
            console.warn('found no [activeCamera], set one for better experience!');
            return null;
        }
        return camera;
    }

    /**
     * 应用修正后的位置
     * @param target 目标
     */
    protected applyPositionToTarget(target: TransformNode) {
        const world = target.parent ? this._matrix.copyFrom(target.parent.getWorldMatrix()) : Matrix.IdentityReadOnly;
        world.invert();
        const pos = this._vector.copyFrom(this.currentPosition);
        Vector3.TransformCoordinatesToRef(pos, world, pos);
        target.position.set(pos.x, pos.y, pos.z);
    }

}

export { AbstractDragLimit };